﻿using DG.Tweening;
using PP.Basic.Pool;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

using Object = UnityEngine.Object;

namespace PP.Basic
{
    /// <summary>
    /// 音频播放管理器
    /// </summary>
    public class Audio : SingletonUnsafe<Audio>
    {
        private const string Tag = nameof(Audio);

        public class AudioPaths
        {
            public const string DefaultBgMusic = "Assets/Production/Basic/Res/Audio/DefaultBgMusic.mp3";
        };

        private class StorageKeys
        {
            public const string IsBgMusicOn = "Audio.IsBgMusicOn";
            public const string IsEffectOn = "Audio.IsEffectOn";
            public const string IsClickSoundOnPrefKey = "Audio.IsClickSoundOn";
            public const string IsVibrateOn = "Audio.IsVibrateOn";
            public const string BgMusicPath = "Audio.BgMusicPath";
        };

        // Private Variables Begin
        private bool? _isBgMusicOn;
        public bool IsBgMusicOn
        {
            get
            {
                if (_isBgMusicOn == null)
                    _isBgMusicOn = LoadBgMusicOn();
                return _isBgMusicOn.Value;
            }
        }

        private AudioSource _bgMusicAudioSource;
        public AudioSource BgMusicAudioSource => _bgMusicAudioSource;

        private string _bgMusicPath;
        public string BgMusicPath
        {
            get
            {
                if (_bgMusicPath == null)
                {
                    _bgMusicPath = LoadBgMusicPath();
                    if (string.IsNullOrEmpty(_bgMusicPath))
                    {
                        _bgMusicPath = AudioPaths.DefaultBgMusic;
                        SaveBgMusicPath(_bgMusicPath);
                    }
                }
                return _bgMusicPath;
            }
        }


        private bool? _isEffectOn;
        /// <summary>
        /// 游戏音效开关
        /// </summary>
        public bool IsEffectOn
        {
            get
            {
                if (_isEffectOn == null)
                {
                    _isEffectOn = LoadEffectOn();
                }
                return (bool)_isEffectOn;
            }
        }

        private bool? _isClickSoundOn;
        /// <summary>
        /// 点击音效开关
        /// </summary>
        public bool IsClickSoundOn
        {
            get
            {
                if (_isClickSoundOn == null)
                {
                    _isClickSoundOn = LoadClickSoundOn();
                }
                return (bool)_isClickSoundOn;
            }
        }


        private bool? _isVibrateOn;
        /// <summary>
        /// 震动开关
        /// </summary>
        public bool IsVibrateOn
        {
            get
            {
                if (_isVibrateOn == null)
                {
                    _isVibrateOn = LoadVibrateOn();
                }
                return (bool)_isVibrateOn;
            }
        }

        // 音频片段缓存
        private Dictionary<string, AudioClip> _audioClipDict = new Dictionary<string, AudioClip>();

        // 播放任务缓存
        private Dictionary<object/*audioTarget*/, AudioPlayTask> _playTaskDict = new Dictionary<object, AudioPlayTask>();
        // Private Variables End

        // Public Methods Begin
        public void StartBgMusic()
        {
            if (IsBgMusicOn)
            {
                SetBgMusicOn(true);
            }
        }

        /// <summary>
        /// 设置背景音乐
        /// </summary>
        /// <param name="bgMusicPath">背景音乐完整路径</param>
        /// <param name="shouldPlayNow"></param>
        public void SetBgMusic(string bgMusicPath, bool shouldPlayNow = false)
        {
            if (string.IsNullOrEmpty(bgMusicPath))
                return;

            if (_bgMusicPath != bgMusicPath)
            {
                _bgMusicPath = bgMusicPath;
                SaveBgMusicPath(bgMusicPath);
            }
            SetBgMusicOn(shouldPlayNow);
        }

        /// <summary>
        /// 开始或停止播放背景音乐并记录状态。
        /// </summary>
        /// <param name="isOn"></param>
        public void SetBgMusicOn(bool isOn)
        {
            bool isBgMusicOn = IsBgMusicOn;
            if (isBgMusicOn != isOn)
            {
                isBgMusicOn = isOn;
                _isBgMusicOn = isOn;
                SaveBgMusicOn(isOn);
            }

            if (isBgMusicOn)
            {
                PlayBgMusic();
            }
            else
            {
                StopBgMusic();
            }
        }

        public void SetEffectOn(bool isOn)
        {
            if (IsEffectOn != isOn)
            {
                _isEffectOn = isOn;
                SaveEffectOn(isOn);
            }
        }

        public void SetVibrateOn(bool isOn)
        {
            if (IsVibrateOn != isOn)
            {
                _isVibrateOn = isOn;
                SaveVibrateOn(isOn);
            }
        }

        public void SetClickSoundOn(bool isOn)
        {
            if (IsClickSoundOn != isOn)
            {
                _isClickSoundOn = isOn;
                SaveClickSoundOn(isOn);
            }
        }

        /// <summary>
        /// 获取并缓存音频片段
        /// </summary>
        /// <param name="audioPath">Addressabe路径</param>
        /// <returns></returns>
        public AudioClip GetAudioClip(string audioPath)
        {
            if (string.IsNullOrEmpty(audioPath))
            {
                Debug.LogError($"{Tag}.PlayAudio# audioPath is null or empty.");
                return null;
            }

            // 从缓存中获取音频
            if (!_audioClipDict.TryGetValue(audioPath, out AudioClip clip))
            {
                // 缓存中没有，从资源中加载
                clip = Resources.Load<AudioClip>(audioPath);
                // 缓存音频
                _audioClipDict[audioPath] = clip;
            }

            if (clip == null)
            {
                Debug.LogError($"{Tag}.PlayAudio# clip is null, audioPath: {audioPath}");
            }
            return clip;
        }

        public void ReleaseAudio(string audioPath)
        {
            if (string.IsNullOrEmpty(audioPath))
                return;

            if (_audioClipDict.TryGetValue(audioPath, out AudioClip clip))
            {
                //Addressable.ReleaseAsset(clip);
                _audioClipDict.Remove(audioPath);
            }
        }

        public void ReleaseAudio(AudioClip clip)
        {
            string audioPathToRemove = null;
            foreach ((string audioPath, AudioClip audioClip) in _audioClipDict)
            {
                if (clip == audioClip)
                {
                    audioPathToRemove = audioPath;
                    break;
                }
            }

            if (audioPathToRemove != null)
            {
                ReleaseAudio(audioPathToRemove);
            }
        }

        public void ReleaseAllAudios()
        {
            foreach ((string audioPath, AudioClip audioClip) in _audioClipDict)
            {
                //Addressable.ReleaseAsset(audioClip);
            }
            _audioClipDict.Clear();
        }

        /// <summary>
        /// 创建一个新对象和音频组件用于播放音频，只缓存音频片段，不缓存音频对象。
        /// </summary>
        /// <param name="audioPath">Addressable路径</param>
        /// <param name="isPlayNow">是否立即播放</param>
        /// <param name="isLoop">是否循环</param>
        /// <returns>AudioSouce音频播放组件，由调用者维护</returns>
        public AudioSource CreateAudio(string audioPath, bool isPlayNow = true, bool isLoop = false)
        {
            if (string.IsNullOrEmpty(audioPath))
            {
                Debug.LogError($"{Tag}.CreateAudio# audioPath is null or empty.");
                return null;
            }

            // 创建音频播放对象和组件
            var co = CreateAudioSource();
            co.gameObject.SetActive(true);
            var trans = co.gameObject.transform;
            trans.SetAsLastSibling();
            trans.position = Vector3.zero;

            var clip = GetAudioClip(audioPath);
            co.clip = clip;
            co.loop = isLoop;

            if (isPlayNow)
                co.Play();
            else
                co.Stop();
            return co;
        }

        /// <summary>
        /// 播放音效
        /// </summary>
        /// <param name="audioPath">音频资源路径</param>
        /// <param name="isLoop">是否循环播放</param>
        /// <param name="autoReleaseAudioClip">
        /// true, 则在音效播放完后，自动是否资源；
        /// false, 则需要自行调用 <see cref="ReleaseAudio(string)"/> 方法来释放资源；
        /// isLoop: true 时，autoRelease 无效，需要调用 <see cref="ReleaseAudio(string)"/> 方法来释放资源；
        /// </param>
        /// <returns>AudioPlayTask，可以用来控制播放</returns>
        public AudioPlayTask PlayAudio(string audioPath, bool isLoop = false, bool autoReleaseAudioClip = false, Action<AudioPlayTask> onComplete = null, Object targetObject = null, bool forcePlay = false)
        {
            AudioPlayTask playTask = new AudioPlayTask();
            do
            {
                // 音效开关关闭，且非强制播放，则不播放
                if (!IsEffectOn && !forcePlay)
                    break;

                var clip = GetAudioClip(audioPath);

                var co = Pool.Get();
                co.clip = clip;
                co.loop = isLoop;
                co.Play();
                playTask.TaskKey = audioPath;
                playTask.AudioSource = co;
                _playTaskDict[audioPath] = playTask;
                if (!isLoop)
                {
                    DOVirtual.DelayedCall(clip.length, () =>
                    {
                        // 完成任务
                        var taskExists = _playTaskDict.Remove(audioPath, out var task);

                        // 无目标对象或目标对象依然存活，则执行回调
                        var isTargetNullOrAlive = (object)targetObject == null || targetObject; // isNull || isAlive
                        if (isTargetNullOrAlive && taskExists)
                        {
                            onComplete?.Invoke(task);
                        }

                        if (autoReleaseAudioClip)
                        {
                            ReleaseAudio(audioPath);
                        }
                        Pool.Release(co);
                    });
                }
            } while (false);
            return playTask;
        }

        /// <summary>
        /// 直接播放音频对象，默认：不循环 + 不释放
        /// </summary>
        public AudioPlayTask PlayAudio(AudioClip audioClip, bool isLoop = false, bool autoReleaseAudioClip = false, Action<AudioPlayTask> onComplete = null, Object targetObject = null, bool forcePlay = false)
        {
            AudioPlayTask playTask = new AudioPlayTask();
            do
            {
                if (!audioClip)
                {
                    Debug.LogError($"{Tag}.PlayAudio# audioClip is null.");
                    break;
                }

                // 音效开关关闭，且非强制播放，则不播放
                if (!IsEffectOn && !forcePlay)
                    break;

                var co = Pool.Get();
                co.clip = audioClip;
                co.loop = isLoop;
                co.Play();
                playTask.TaskKey = audioClip;
                playTask.AudioSource = co;
                _playTaskDict[audioClip] = playTask;
                if (!isLoop)
                {
                    DOVirtual.DelayedCall(audioClip.length, () =>
                    {
                        // 完成任务
                        var taskExists = _playTaskDict.Remove(audioClip, out var task);

                        // 无目标对象或目标对象依然存活，则执行回调
                        var isTargetNullOrAlive = (object)targetObject == null || targetObject; // isNull || isAlive
                        if (isTargetNullOrAlive && taskExists)
                        {
                            onComplete?.Invoke(task);
                        }

                        if (autoReleaseAudioClip)
                        {
                            //Addressable.ReleaseAsset(audioClip);
                        }
                        Pool.Release(co);
                    });
                }
            } while (false);
            return playTask;
        }

        /// <summary>
        /// 停止音频播放
        /// </summary>
        /// <param name="audioPath"></param>
        public void StopAudio(string audioPath)
        {
            StopAudio((object)audioPath);
        }

        public void StopAudio(AudioClip audioClip)
        {
            StopAudio((object)audioClip);
        }

        public void StopAudio(AudioPlayTask playTask)
        {
            StopAudio(playTask.TaskKey);
        }

        public void StopAudio(object taskKey)
        {
            // 参数校验
            if (taskKey is not string && taskKey is not AudioClip)
            {
                Debug.LogError($"{Tag}.StopAudio# taskKey is not string or AudioClip.");
                return;
            }

            // 移除播放任务
            if (_playTaskDict.Remove(taskKey, out var playTask))
            {
                // 停止音频播放
                playTask.AudioSource.Stop();
            }
        }


        /// <summary>
        /// 播放点击音效 (独立于游戏音效)
        /// </summary>
        public void PlayClick(string audioPath = null)
        {
            if (!string.IsNullOrEmpty(audioPath) && IsClickSoundOn)
            {
                PlayAudio(audioPath, forcePlay: true);
            }
        }
        public void PlayClick(AudioClip audioClip)
        {
            if (audioClip != null && IsClickSoundOn)
            {
                PlayAudio(audioClip, forcePlay: true);
            }
        }


        /// <summary>
        /// 震动设备
        /// </summary>
        public void PlayVibrate(float duration = 0.4f)
        {
            if (!IsVibrateOn)
                return;

#if UNITY_ANDROID || UNITY_IOS
            Handheld.Vibrate();
#endif
        }
        // Public Methods End

        // Private Methods Begin
        private void PlayBgMusic()
        {
            StopBgMusic();
            
            var bgMusicAudioClip = Resources.Load<AudioClip>(BgMusicPath);
            if (bgMusicAudioClip != null)
            {
                _bgMusicAudioSource = Pool.Get();
                _bgMusicAudioSource.clip = bgMusicAudioClip;
                _bgMusicAudioSource.loop = true;
            }
        }

        private void StopBgMusic()
        {
            if (_bgMusicAudioSource == null)
                return;
            ReleaseAudioSource(_bgMusicAudioSource);
            _bgMusicAudioSource = null;
        }

        private void ReleaseAudioSource(AudioSource audioSource, bool releaseAudioClip = false)
        {
            if (!audioSource)
                return;

            if (releaseAudioClip && audioSource.clip != null)
            {
                ReleaseAudio(audioSource.clip);
            }
            Pool.Release(audioSource);
        }

        private bool LoadBgMusicOn()
        {
            return PlayerPrefs.GetInt(StorageKeys.IsBgMusicOn, 1) == 1;
        }

        private void SaveBgMusicOn(bool isOn)
        {
            PlayerPrefs.SetInt(StorageKeys.IsBgMusicOn, isOn ? 1 : 0);
        }

        private bool LoadEffectOn()
        {
            return PlayerPrefs.GetInt(StorageKeys.IsEffectOn, 1) == 1;
        }

        private bool LoadVibrateOn()
        {
            return PlayerPrefs.GetInt(StorageKeys.IsVibrateOn, 1) == 1;
        }

        private void SaveEffectOn(bool isOn)
        {
            PlayerPrefs.SetInt(StorageKeys.IsEffectOn, isOn ? 1 : 0);
        }

        private void SaveVibrateOn(bool isOn)
        {
            PlayerPrefs.SetInt(StorageKeys.IsVibrateOn, isOn ? 1 : 0);
        }

        private string LoadBgMusicPath()
        {
            return PlayerPrefs.GetString(StorageKeys.BgMusicPath);
        }

        private void SaveBgMusicPath(string bgMusicPath)
        {
            PlayerPrefs.SetString(StorageKeys.BgMusicPath, bgMusicPath ?? "");
        }


        private void SaveClickSoundOn(bool isOn)
        {
            PlayerPrefs.SetInt(StorageKeys.IsClickSoundOnPrefKey, isOn ? 1 : 0);
        }
        private bool LoadClickSoundOn()
        {
            return PlayerPrefs.GetInt(StorageKeys.IsClickSoundOnPrefKey, 1) == 1;
        }
        // Private Methods End

        // Private Pool Begin
        private const string AudioRootGoName = "~Audio";

        private Transform _audioRootTrans;
        public Transform AudioRootTrans
        {
            get
            {
                if (_audioRootTrans)
                    return _audioRootTrans;

                var go = GameObject.Find(AudioRootGoName);
                if (go == null)
                {
                    go = new GameObject(AudioRootGoName);
                    GameObject.DontDestroyOnLoad(go);
                }
                _audioRootTrans = go.transform;
                return _audioRootTrans;
            }
        }

        private ObjectPool<AudioSource> _pool;
        private ObjectPool<AudioSource> Pool
        {

            get
            {
                if (_pool != null)
                    return _pool;

                _pool = new ObjectPool<AudioSource>
                (
                    createFunc: () =>
                    {
                        return CreateAudioSource();
                    },
                    actionOnGet: (AudioSource co) =>
                    {
                        if (!co)
                        {
                            // 原有对象被销毁了，重新创建对象
                            co = CreateAudioSource();
                        }

                        // 显示对象
                        co.gameObject.SetActive(true);

                        var trans = co.gameObject.transform;
                        trans.SetAsLastSibling();
                        trans.position = Vector3.zero;
                    },
                    actionOnRelease: (AudioSource co) =>
                    {
                        co.Stop();
                        co.clip = null;
                        // 隐藏对象
                        co.gameObject.SetActive(false);
                    },
                    actionOnDestroy: (AudioSource co) =>
                    {
                        // 销毁对象
                        if (co?.gameObject)
                            GameObject.Destroy(co.gameObject);
                    },
                    collectionCheck: false,
                    defaultCapacity: 5,
                    maxSize: 150
                );
                return _pool;
            }
        }

        private static AudioSource CreateAudioSource()
        {
            GameObject go = new GameObject("AudioGo"); // create the temp object
            go.transform.SetParent(Instance.AudioRootTrans);
            AudioSource co = go.AddComponent<AudioSource>(); // add an audio source
            return co; // return the AudioSource reference
        }
        // Private Pool End

    }
}
